// -*- mode:c++ -*-

// Copyright (c) 2015 RISC-V Foundation
// Copyright (c) 2016-2017 The University of Virginia
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: Robert Scheffel

////////////////////////////////////////////////////////////////////
//
// Integer instructions
//

def template Imm32Declare {{
namespace Rv32
{

//
// Static instruction class for "%(mnemonic)s".
//
class %(class_name)s : public %(base_class)s
{
  public:
    /// Constructor.
    %(class_name)s(MachInst machInst);
    Fault execute(ExecContext *, Trace::InstRecord *) const override;
    std::string generateDisassembly(Addr pc,
        const SymbolTable *symtab) const override;
};

} // namespace Rv32
}};

def template Imm32Constructor {{
Rv32::%(class_name)s::%(class_name)s(MachInst machInst)
    : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
{
    %(constructor)s;
    %(imm_code)s;
}
}};

def template Imm32Execute {{
Fault
Rv32::%(class_name)s::execute(
    ExecContext *xc, Trace::InstRecord *traceData) const
{
    Fault fault = NoFault;

    %(op_decl)s;
    %(op_rd)s;
    if (fault == NoFault) {
        %(code)s;
        if (fault == NoFault) {
            %(op_wb)s;
        }
    }
    return fault;
}

std::string
Rv32::%(class_name)s::generateDisassembly(Addr pc,
        const SymbolTable *symtab) const
{
    std::vector<RegId> indices = {%(regs)s};
    std::stringstream ss;
    ss << mnemonic << ' ';
    for (const RegId& idx: indices)
        ss << registerName(idx) << ", ";
    ss << imm;
    return ss.str();
}
}};

def template Branch32Declare {{
namespace Rv32
{

//
// Static instruction class for "%(mnemonic)s".
//
class %(class_name)s : public %(base_class)s
{
  public:
    /// Constructor.
    %(class_name)s(MachInst machInst);
    Fault execute(ExecContext *, Trace::InstRecord *) const override;

    std::string
    generateDisassembly(Addr pc, const SymbolTable *symtab) const override;

    RiscvISA::PCState
    branchTarget(const RiscvISA::PCState &branchPC) const override;

    using StaticInst::branchTarget;
};

} // namespace Rv32
}};

def template Branch32Execute {{
Fault
Rv32::%(class_name)s::execute(ExecContext *xc,
    Trace::InstRecord *traceData) const
{
    Fault fault = NoFault;

    %(op_decl)s;
    %(op_rd)s;
    if (fault == NoFault) {
        %(code)s;
        if (fault == NoFault) {
            %(op_wb)s;
        }
    }
    return fault;
}

RiscvISA::PCState
Rv32::%(class_name)s::branchTarget(const RiscvISA::PCState &branchPC) const
{
    return branchPC.pc() + imm;
}

std::string
Rv32::%(class_name)s::generateDisassembly(Addr pc,
        const SymbolTable *symtab) const
{
    std::vector<RegId> indices = {%(regs)s};
    std::stringstream ss;
    ss << mnemonic << ' ';
    for (const RegId& idx: indices)
        ss << registerName(idx) << ", ";
    ss << imm;
    return ss.str();
}
}};

def template Jump32Declare {{
namespace Rv32
{

//
// Static instruction class for "%(mnemonic)s".
//
class %(class_name)s : public %(base_class)s
{
  public:
    /// Constructor.
    %(class_name)s(MachInst machInst);
    Fault execute(ExecContext *, Trace::InstRecord *) const override;

    std::string
    generateDisassembly(Addr pc, const SymbolTable *symtab) const override;

    RiscvISA::PCState
    branchTarget(ThreadContext *tc) const override;

    using StaticInst::branchTarget;
};

} // namespace Rv32
}};

def template Jump32Execute {{
Fault
Rv32::%(class_name)s::execute(
    ExecContext *xc, Trace::InstRecord *traceData) const
{
    Fault fault = NoFault;

    %(op_decl)s;
    %(op_rd)s;
    if (fault == NoFault) {
        %(code)s;
        if (fault == NoFault) {
            %(op_wb)s;
        }
    }
    return fault;
}

RiscvISA::PCState
Rv32::%(class_name)s::branchTarget(ThreadContext *tc) const
{
    PCState pc = tc->pcState();
    pc.set((tc->readIntReg(_srcRegIdx[0].index()) + imm)&~0x1);
    return pc;
}

std::string
Rv32::%(class_name)s::generateDisassembly(Addr pc,
        const SymbolTable *symtab) const
{
    std::vector<RegId> indices = {%(regs)s};
    std::stringstream ss;
    ss << mnemonic << ' ';
    for (const RegId& idx: indices)
        ss << registerName(idx) << ", ";
    ss << imm;
    return ss.str();
}
}};

def format R32Op(code, *opt_flags) {{
    iop = InstObjParams(name, Name, 'RegOp', code, opt_flags)
    header_output = Basic32Declare.subst(iop)
    decoder_output = Basic32Constructor.subst(iop)
    decode_block = Basic32Decode.subst(iop)
    exec_output = Basic32Execute.subst(iop)
}};

def format I32Op(code, imm_type='int32_t', *opt_flags) {{
    regs = ['_destRegIdx[0]','_srcRegIdx[0]']
    iop = InstObjParams(name, Name, 'ImmOp<%s>' % imm_type,
        {'code': code,
         'imm_code': 'imm = mbits<uint64_t>(sext<12>(IMM12), 31, 0);',
         'regs': ','.join(regs)}, opt_flags)
    header_output = Imm32Declare.subst(iop)
    decoder_output = Imm32Constructor.subst(iop)
    decode_block = Basic32Decode.subst(iop)
    exec_output = Imm32Execute.subst(iop)
}};

def format B32Op(code, *opt_flags) {{
    imm_code = """
                imm = BIMM12BITS4TO1 << 1  |
                      BIMM12BITS10TO5 << 5 |
                      BIMM12BIT11 << 11    |
                      IMMSIGN << 12;
                imm = mbits<uint64_t>(sext<13>(imm), 31, 0);
               """
    regs = ['_srcRegIdx[0]','_srcRegIdx[1]']
    iop = InstObjParams(name, Name, 'ImmOp<int32_t>',
        {'code': code, 'imm_code': imm_code,
         'regs': ','.join(regs)}, opt_flags)
    header_output = Branch32Declare.subst(iop)
    decoder_output = Imm32Constructor.subst(iop)
    decode_block = Basic32Decode.subst(iop)
    exec_output = Branch32Execute.subst(iop)
}};

def format U32Op(code, *opt_flags) {{
    regs = ['_destRegIdx[0]']
    iop = InstObjParams(name, Name, 'ImmOp<uint32_t>',
        {'code': code, 'imm_code': 'imm = IMM20 << 12;',
         'regs': ','.join(regs)}, opt_flags)
    header_output = Imm32Declare.subst(iop)
    decoder_output = Imm32Constructor.subst(iop)
    decode_block = Basic32Decode.subst(iop)
    exec_output = Imm32Execute.subst(iop)
}};

def format J32Op(code, *opt_flags) {{
    imm_code = """
                imm = UJIMMBITS10TO1 << 1   |
                      UJIMMBIT11 << 11      |
                      UJIMMBITS19TO12 << 12 |
                      IMMSIGN << 20;
                imm = sext<21>(imm);
               """
    pc = 'pc.set(pc.pc() + imm);'
    regs = ['_destRegIdx[0]']
    iop = InstObjParams(name, Name, 'ImmOp<int32_t>',
        {'code': code, 'imm_code': imm_code,
         'regs': ','.join(regs)}, opt_flags)
    header_output = Branch32Declare.subst(iop)
    decoder_output = Imm32Constructor.subst(iop)
    decode_block = Basic32Decode.subst(iop)
    exec_output = Branch32Execute.subst(iop)
}};

def format Jump32(code, *opt_flags) {{
    regs = ['_destRegIdx[0]', '_srcRegIdx[0]']
    iop = InstObjParams(name, Name, 'ImmOp<int32_t>',
        {'code': code, 'imm_code': 'imm = sext<12>(IMM12);',
         'regs': ','.join(regs)}, opt_flags)
    header_output = Jump32Declare.subst(iop)
    decoder_output = Imm32Constructor.subst(iop)
    decode_block = Basic32Decode.subst(iop)
    exec_output = Jump32Execute.subst(iop)
}};

def format System32Op(code, *opt_flags) {{
    iop = InstObjParams(name, Name, 'SystemOp', code, opt_flags)
    header_output = Basic32Declare.subst(iop)
    decoder_output = Basic32Constructor.subst(iop)
    decode_block = Basic32Decode.subst(iop)
    exec_output = Basic32Execute.subst(iop)
}};

def format CSR32Op(code, *opt_flags) {{
    iop = InstObjParams(name, Name, 'CSROp', code, opt_flags)
    header_output = Basic32Declare.subst(iop)
    decoder_output = Basic32Constructor.subst(iop)
    decode_block = Basic32Decode.subst(iop)
    exec_output = Basic32Execute.subst(iop)
}};
